package errc

import (
	"fmt"
	"runtime"
)

// -1--999 system common error code
const (
	Failed       = -1 //失败
	Success      = 0  //成功
	InternalErr  = 1  //内部错误处
	NeedLogin    = 2  //需要登录
	AccessDenied = 3  //无权限
	NotFound     = 4  //未找到资源
	ParamInvalid = 5  //参数校验

	// AuthInternalErr auth的验证
	AuthInternalErr      = 10
	AuthRequired         = 11
	AuthExpired          = 12
	AuthInvalid          = 13
	AuthSignatureInvalid = 14
)

var Lang = "EN"

func SetLang(lang string) {
	Lang = lang
}

// meesage的处理
type MutiMsg struct {
	CN string `json:"cn"`
	EN string `json:"en"`
}

var MessagesCN = map[int]string{
	Failed:               "失败",
	Success:              "成功",
	InternalErr:          "内部错误",
	NeedLogin:            "未登录",
	AccessDenied:         "无权限",
	ParamInvalid:         "参数验证无效",
	NotFound:             "资源不存在",
	AuthInternalErr:      "权限内部错误",
	AuthRequired:         "权限请求头必传",
	AuthExpired:          "权限过期",
	AuthInvalid:          "权限无效",
	AuthSignatureInvalid: "权限签名无效",
}

var Messages = map[int]string{
	Failed:               "Failed",
	Success:              "Ok",
	InternalErr:          "Internal error",
	NeedLogin:            "Login required",
	AccessDenied:         "Auth invalid",
	ParamInvalid:         "Param validator invalid",
	NotFound:             "Not found",
	AuthInternalErr:      "Auth internal error",
	AuthRequired:         "Auth Authorization header required",
	AuthExpired:          "Auth expired",
	AuthInvalid:          "Auth invalid",
	AuthSignatureInvalid: "Auth signature invalid",
}

func GetMessage(code int) string {
	switch Lang {
	case "CN":
		return MessagesCN[code]
	default:
		return Messages[code]
	}
}

var (
	ErrFailed               = NewError(Failed, GetMessage(Failed))
	ErrInternalErr          = NewError(InternalErr, GetMessage(InternalErr))
	ErrNeedLogin            = NewError(NeedLogin, GetMessage(NeedLogin))
	ErrAccessDenied         = NewError(AccessDenied, GetMessage(AccessDenied))
	ErrParamInvalid         = NewError(ParamInvalid, GetMessage(ParamInvalid))
	ErrNotFound             = NewError(NotFound, GetMessage(NotFound))
	ErrAuthInternalErr      = NewError(AuthInternalErr, GetMessage(AuthInternalErr))
	ErrAuthRequired         = NewError(AuthRequired, GetMessage(AuthRequired))
	ErrAuthExpired          = NewError(AuthExpired, GetMessage(AuthExpired))
	ErrAuthInvalid          = NewError(AuthInvalid, GetMessage(AuthInvalid))
	ErrAuthSignatureInvalid = NewError(AuthSignatureInvalid, GetMessage(AuthSignatureInvalid))
)

type Error struct {
	Code    int      `json:"code" xml:"code"`
	Message string   `json:"message" xml:"message"`
	Stack   []string `json:"-" xml:"-"`
}

func NewError(code int, msg string) Error {
	return Error{Code: code, Message: msg, Stack: make([]string, 0, 1)}
}

// With 构建一个错误的信息
func (e Error) With(ch, en string) Error {
	if Lang == "CN" {
		if ch == "" {
			ch = GetMessage(e.Code)
		}
		return Error{Code: e.Code, Message: ch, Stack: make([]string, 0, 1)}
	}
	if en == "" {
		en = GetMessage(e.Code)
	}
	return Error{Code: e.Code, Message: en, Stack: make([]string, 0, 1)}
}

// WithEnum 通过枚举来进行翻译的操作
func (e Error) WithEnum(mt MutiMsg) Error {
	if Lang == "CN" {
		return Error{Code: e.Code, Message: mt.CN, Stack: make([]string, 0, 1)}
	}
	return Error{Code: e.Code, Message: mt.EN, Stack: make([]string, 0, 1)}
}

func (e Error) Error() string {
	return fmt.Sprintf("code:%d message:%s statck:%v", e.Code, e.Message, e.Stack)
}

func (e Error) MultiErr(err error) Error {
	e.Message = GetMessage(e.Code)
	if err != nil {
		if e1, ok := err.(Error); ok {
			e.Message += " " + e1.Message
			return e.addStack(e1.Stack...)
		} else {
			e.Message += " " + err.Error()
		}
	}
	return e
}

func (e Error) MultiMsg(v ...interface{}) Error {
	e.Message = GetMessage(e.Code)
	msg := fmt.Sprint(v...)
	if msg != "" {
		e.Message += " " + msg
	}
	return e
}

func (e Error) CodeMsg() (int, string) {
	return e.Code, e.Message
}

func (e Error) addStack(v ...string) Error {
	e.Stack = append(e.Stack, v...)
	return e
}

func Message(msg string) Error {
	return NewError(Failed, msg)
}

func WithStack(err error) error {
	if err == nil {
		return nil
	}
	frames := runtime.CallersFrames(callers())
	stack := ""
	frame, more := frames.Next()
	if more {
		stack = fmt.Sprintf("\n%s:%d %s", frame.File, frame.Line, frame.Function)
	}

	if e, ok := err.(Error); ok {
		return e.addStack(stack)
	}
	return NewError(Failed, err.Error()).addStack(stack)
}

func callers() []uintptr {
	const depth = 8
	var pcs [depth]uintptr
	n := runtime.Callers(3, pcs[:])
	return pcs[0:n]
}

func ByFailed(err error) error {
	if err == nil {
		return nil
	}
	return NewError(Failed, err.Error())
}
func ByInternal(err error) error {
	if err == nil {
		return nil
	}
	return NewError(InternalErr, err.Error())
}
func ByNeedLogin(err error) error {
	if err == nil {
		return nil
	}
	return NewError(NeedLogin, err.Error())
}
func ByAccess(err error) error {
	if err == nil {
		return nil
	}
	return NewError(AccessDenied, err.Error())
}
func ByNotFound(err error) error {
	if err == nil {
		return nil
	}
	return NewError(NotFound, err.Error())
}
func ByParam(err error) error {
	if err == nil {
		return nil
	}
	return NewError(ParamInvalid, err.Error())
}
func ByTyp(err error, code int) error {
	if err == nil {
		return nil
	}
	return NewError(code, err.Error())
}
